#ifndef _AS_ENTITY_H_
#define _AS_ENTITY_H_

#include "ASObject.h"
#include "ASEntityAttribute.h"
#include "ASMessage.h"


NS_AS_BEGIN

class ASComponent;
//class ASEntityInitData
//{
//public:
//	ASEntityInitData(int i=0,int f=0);
//	~ASEntityInitData();
//	int _nAttrsNumI;
//	int _nAttrsNumF;
//};

class ASEntityMobileData
{
public:
	ASEntityMobileData(const Vector2D& heading=Vector2D(0,1),double mass=99999999,double maxforce=0,double maxspeed=0,double turnrate=Pi)
		:_vHeading(heading)
		,_vSide(heading.Perp())
		,_dMass(mass)
		,_dMaxspeed(maxspeed)
		,_dMaxForce(maxforce)
		,_dMaxTurnRate(turnrate)
	{
		ASAssert(_dMass>AS_VECTOR_MIN,"mass invalid");
		_dMassInv = 1/_dMass;
	}

	Vector2D _vHeading;
	Vector2D _vSide;
	double _dMass;
	double _dMassInv;
	double _dMaxspeed;
	double _dMaxForce;
	double _dMaxTurnRate;
	
};

class ASEntity:public ASObject
{
public:
	ASEntity(int nAttrsNumI = 0,int nAttrsNumF = 0,const ASEntityMobileData* pMobiledata=nullptr);
	virtual ~ASEntity();

	//sigslot::signal1<ASEntity*> _SignalDestory;
#ifdef ASMEMORY
	void* operator new(size_t size)  
    {  
		ASLog("ASEntity::operator new");
        return malloc(size);  
    }  
  
    void* operator new(size_t size, const std::nothrow_t& nothrow_value)  
    {  
        ASLog("ASEntity::operator new nothrow");  
        return malloc(size);  
    } 

	void operator delete(void* p)  
    {  
		ASLog("ASEntity::operator delete");  
        free(p);  
    }   
#endif
	virtual void destory();

	void addComponent(ASComponent* component);
	ASComponent* getComponent(int compName);
	bool removeComponent(int compName);
	
	bool addRefuseComponent(int compName);
	bool removeRefuseComponent(int compName);

	void setMobileData(const ASEntityMobileData* pMobiledata);
	void entityUpdate(float dt);
	virtual void update(float dt);
	virtual void updateData(){};//deal or reset data every update frame
	virtual void onHandleMessage(void* msg){};
	bool getActive(){return _bActive;};
	const Vector2D& getPosition(){return _vPosition;};
	virtual void setPosition(const Vector2D& pos){_vPosition = pos;};
	int getBoundRadius(){return _iBoundRadius;}
	int getDetechRadius(){return _iDetechRadius;}
	int getAttackRadius(){return _iAttackRadius;}
	void setBoundRadius(unsigned int radius){_iBoundRadius = radius;};
	void setDetechRadius(unsigned int radius){_iDetechRadius = radius;}
	void setAttackRadius(unsigned int radius){_iAttackRadius = radius;}
	void setCollideFlag(unsigned int collideflag){_iCollideFlag = collideflag;}
	unsigned int getCollideFlag(){return _iCollideFlag;};
	bool checkCollideFlag(const ASEntity* other){return (_iCollideFlag&other->_iCollideFlag)!=0;}
	void tag(){_bTag = true;}
	void unTag(){_bTag = false;}
	bool isTaged(){return _bTag;}

	Vector2D getVelocity()const{return _vVelocity;}
	void setVelocity(const Vector2D& NewVel){_vVelocity = NewVel;}
	double getMass()const{return _dMass;}
	double getMassInv()const {return _dMassInv;}
	Vector2D getSide()const{return _vSide;}
	double getMaxSpeed()const{return _dMaxSpeed;}                       
	void setMaxSpeed(double new_speed){_dMaxSpeed = new_speed;}
	bool isSpeedMaxedOut()const{return _dMaxSpeed*_dMaxSpeed >= _vVelocity.LengthSq();}
	double getSpeed()const{return _vVelocity.Length();}
	double getSpeedSq()const{return _vVelocity.LengthSq();} 
	Vector2D getHeading()const{return _vHeading;}
	void setHeading(Vector2D new_heading);
	bool rotateHeadingToFacePosition(Vector2D target);
	double getMaxTurnRate()const{return _dMaxTurnRate;}
	void setMaxTurnRate(double val){_dMaxTurnRate = val;}	
	double getMaxForce()const{return _dMaxForce;}
	void setMaxForce(double mf){_dMaxForce = mf;}

	void setAlignOrder(int order){_iAlignOrder = order;};
	int getAlignOrder(){return _iAlignOrder;}
	void setTypeID(int type){_iTypeID = type;}
	int getTypeID(){return _iTypeID;}
	//////////////////////////////////////////////////////
	ASEntityAttribute* getAttribute(){return _pAttribute;}
	int getAttributeI(unsigned int index){return _pAttribute->getAttributeI(index);};
	void setAttributeI(unsigned int index,int value){_pAttribute->setAttributeI(index,value);};
	ASEntityAttribute::LimitTriggerEnum changeAttributeI(unsigned int index,int value,int min=INT_MIN,int max=INT_MAX,int* pOutValue = nullptr){return _pAttribute->changeAttributeI(index,value,min,max,pOutValue);};
	bool checkAttributeValidI(unsigned int index){_pAttribute->checkAttributeValidI(index);};
	float getAttributeF(unsigned int index){return _pAttribute->getAttributeF(index);};
	void setAttributeF(unsigned int index,float value){_pAttribute->setAttributeF(index,value);};
	ASEntityAttribute::LimitTriggerEnum changeAttributeF(unsigned int index,float value,float min=-FLT_MAX,float max=FLT_MAX,float* pOutValue = nullptr){return _pAttribute->changeAttributeF(index,value,min,max,pOutValue);};
	bool checkAttributeValidF(unsigned int index){_pAttribute->checkAttributeValidF(index);};

	virtual ASEntityAttribute::LimitTriggerEnum changeAttributeIEx(unsigned int index,int value,ASEntity* pSender=nullptr,int* pOutValue = nullptr){return _pAttribute->changeAttributeI(index,value,INT_MIN,INT_MAX,pOutValue);};
	virtual ASEntityAttribute::LimitTriggerEnum changeAttributeFEx(unsigned int index,float value,ASEntity* pSender=nullptr,float* pOutValue = nullptr){return _pAttribute->changeAttributeF(index,value,-FLT_MAX,FLT_MAX,pOutValue);};
	
	
protected:
	//func
	void setActive(bool active){_bActive = active;};
	void addComponentForce(ASComponent* component);//forbid call outside , may break _mComs loop
	bool removeComponentForce(int compName);//forbid call outside , may break _mComs loop
	void removeAllComponent();//forbid call outside , may break _mComs loop
	bool isComsLocked(){return _bComsLocked;}
	void lockComs(){_bComsLocked = true;}
	void unlockComs(){_bComsLocked = false;}
	//data
	ASEntityAttribute* _pAttribute;
	std::multimap<int,ASComponent*> _mComs;
	std::vector<ASComponent*> _vComsToAdd;
	std::set<int> _sComsRefuse;
	friend class ASMobileComponent;
	friend class ASSteerComponent;
	Vector2D _vPosition;
	int _iDetechRadius;
	int _iAttackRadius;
	int _iBoundRadius;
	unsigned int _iCollideFlag;//bit flag for different group,example : 0x00000001,0x00000002,
	int _iAlignOrder;//
	int _iTypeID;//entity type,separator different subclass quickly,instead of dynamic_cast/typeid,
	Vector2D _vVelocity;
	Vector2D _vHeading;
	Vector2D _vSide;
	
	double _dMass;
	double _dMassInv;
	double _dMaxSpeed;
	double _dMaxForce;
	double _dMaxTurnRate;//the maximum rate(radians per second) this entity can rotate
	
	bool _bComsLocked;
	bool _bTag;
	bool _bActive;
};

inline void ASEntity::setHeading(Vector2D new_heading)
{
	ASAssert( abs(new_heading.LengthSq() - 1.0) < 0.00001,"");
  
	_vHeading = new_heading;

	//the side vector must always be perpendicular to the heading
	_vSide = _vHeading.Perp();
}

//--------------------------- RotateHeadingToFacePosition ---------------------
//
//  given a target position, this method rotates the entity's heading and
//  side vectors by an amount not greater than m_dMaxTurnRate until it
//  directly faces the target.
//
//  returns true when the heading is facing in the desired direction
//-----------------------------------------------------------------------------
inline bool ASEntity::rotateHeadingToFacePosition(Vector2D target)
{
	Vector2D toTarget = Vec2DNormalize(target - getPosition());

	//first determine the angle between the heading vector and the target
	double angle = acos(_vHeading.Dot(toTarget));

	//return true if the player is facing the target
	if (angle < 0.00001) return true;

	//clamp the amount to turn to the max turn rate
	if (angle > _dMaxTurnRate) angle = _dMaxTurnRate;
  
	//The next few lines use a rotation matrix to rotate the player's heading
	//vector accordingly
	C2DMatrix RotationMatrix;
  
	//notice how the direction of rotation has to be determined when creating
	//the rotation matrix
	RotationMatrix.Rotate(angle * _vHeading.Sign(toTarget));	
	RotationMatrix.TransformVector2Ds(_vHeading);
	RotationMatrix.TransformVector2Ds(_vVelocity);

	//finally recreate m_vSide
	_vSide = _vHeading.Perp();

	return false;
}

NS_AS_END

#endif